package com.csx.ops.web.controller.manager;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.http.server.HttpServerResponse;
import com.csx.ops.core.common.Const;
import com.csx.ops.core.common.ServerResponse;
import com.csx.ops.dao.DbConfig;
import com.csx.ops.dao.dal.auto.t_publish_base_dal;
import com.csx.ops.dao.dal.auto.t_publish_log_base_dal;
import com.csx.ops.dao.dal.auto.t_user_base_dal;
import com.csx.ops.dao.model.auto.t_publish_log_model;
import com.csx.ops.dao.model.auto.t_publish_model;
import com.csx.ops.dao.model.auto.t_user_model;
import com.csx.ops.service.PublishLogService;
import com.csx.ops.service.impl.PublishLogServiceImpl;
import com.csx.ops.service.node.StateEngine;
import com.csx.ops.web.base.SpringMvcController;
import com.csx.ops.web.base.User;
import com.google.common.collect.Maps;
import com.yh.csx.bsf.core.base.Ref;
import com.yh.csx.bsf.core.db.DbHelper;
import com.yh.csx.bsf.core.util.JsonUtils;
import com.yh.csx.bsf.core.util.PropertyUtils;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import lombok.var;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 发布控制器
 * 和发布有关的一系列流程
 * @author yls
 * @date 2020-12-17
 *
 *
 */
@Slf4j
@Controller
@RequestMapping(value="/publish")
public class PublishController extends SpringMvcController {




	@GetMapping("/pageList")
	@ResponseBody
	public ServerResponse  pageList(String username,Integer pageIndex, Integer pageSize){
            t_user_model user=User.getCurrent();
            if(user==null){
                return ServerResponse.createByErrorMessage("用户会话已失效");
            }
			val pageIndex2=(pageIndex == null?1:pageIndex);
			val pageSize2=(pageSize == null?10:pageSize);
			if(Const.ADMIN_ACCOUNT_NAME.equals(user.getRole())) {
                Ref<Integer> totalSize = new Ref<>(0);
                return DbHelper.get(DbConfig.getDbSource(), c -> {
                    Map<String, Object> data = new t_publish_base_dal().getPage(c, username, pageIndex2, pageSize2, totalSize,null);
                    List<t_publish_model> dataList = (List<t_publish_model>) data.get("data");
                    Integer count = (Integer) data.get("count");
                    return ServerResponse.createBySuccess(dataList, "查询成功", count);
                });
            }else{
                Ref<Integer> totalSize = new Ref<>(0);
                return DbHelper.get(DbConfig.getDbSource(), c -> {
                    Map<String, Object> data = new t_publish_base_dal().getPage(c, username, pageIndex2, pageSize2, totalSize,user.getId());
                    List<t_publish_model> dataList = (List<t_publish_model>) data.get("data");
                    Integer count = (Integer) data.get("count");
                    return ServerResponse.createBySuccess(dataList, "查询成功", count);
                });
            }
	}



    /**
     * 详情
     * @param
     * @return
     */
    @GetMapping("/detail")
    @ResponseBody
    public ServerResponse  deatil(@RequestParam(required = true) Integer id){
        return DbHelper.get(DbConfig.getDbSource(), c->{
            t_publish_model user=new t_publish_base_dal().get(c,id);
            return ServerResponse.createBySuccess(user);
        });
    }


    /**
     * 添加
     * @param publishModel
     * @return
     */
    @PostMapping("/save")
    @ResponseBody
    public ServerResponse  save(t_publish_model publishModel){
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }
        return DbHelper.get(DbConfig.getDbSource(), c->{
            publishModel.setCreateTime(new Date());
            publishModel.setUpdateTime(new Date());
            publishModel.setCreateUser(user.getName());
            publishModel.setUpdateUser(user.getName());
            publishModel.setCreateUserId(user.getId());
            publishModel.setUpdateUserId(user.getId());
            boolean rs=new t_publish_base_dal().add(c,publishModel);
            if(rs){
                return ServerResponse.createBySuccessMessage("新增成功");
            }else {
                return ServerResponse.createByErrorMessage("新增失败");
            }
        });
    }


    /**
     * 修改用户信息
     * @param publishModel
     * @return
     */
    @PostMapping("/update")
    @ResponseBody
    public ServerResponse  update(t_publish_model publishModel){
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }
        return DbHelper.get(DbConfig.getDbSource(), c->{
            publishModel.setUpdateTime(new Date());
            publishModel.setUpdateUser(user.getName());
            publishModel.setUpdateUserId(user.getId());
            boolean rs=new t_publish_base_dal().edit(c,publishModel);
            if(rs){
                return ServerResponse.createBySuccessMessage("修改成功");
            }else {
                return ServerResponse.createBySuccessMessage("修改失败");
            }
        });
    }



    /**
     * 删除用户信息
     * @param id  主键id
     * @return
     */
    @PostMapping("/delete")
    @ResponseBody
    public ServerResponse  delete(Integer id){
        return DbHelper.get(DbConfig.getDbSource(), c->{
            boolean rs=new t_publish_base_dal().delete(c,id);
            if(rs){
                return ServerResponse.createBySuccessMessage("修改成功");
            }else {
                return ServerResponse.createBySuccessMessage("修改失败");
            }
        });
    }


    /**
     * 测试发布，只对当前节点生效
     * @param nodeJson  当前节点json
     * @return
     */
    @PostMapping("/testCurrentNode")
    @ResponseBody
    public ServerResponse testCurrentNode(@RequestParam(required = true) String nodeJson){
        //根据json序列化当前节点node
        try {
            StateEngine engine=new StateEngine().getNodefromJson(nodeJson);
            if(engine==null){
                return ServerResponse.createByErrorMessage("序列化出错了");
            }
            engine.getCurrentNode().exec();
            return ServerResponse.createBySuccessMessage("测试成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ServerResponse.createByErrorMessage("测试出错:"+e.getMessage());
        }
    }

    @PostMapping("/generatePublishLogFile")
    @ResponseBody
    public ServerResponse  generatePublishLogFile(@RequestParam(required = true) String nodeJson,
                                                  @RequestParam(required = true) String type,
                                                  @RequestParam(required = true) Integer id){
        String publishTypeDesc;
        //参数校验
        if(!("normal".equals(type))&&!("rollback".equals(type))){
            return ServerResponse.createByErrorMessage("参数类型错误");
        }

        //参数校验
        if("normal".equals(type)){
            publishTypeDesc="正常发布流程";
        }else{
            publishTypeDesc="回滚流程";
        }

        //用户会话校验
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }

        //发布记录校验
        t_publish_model publishModel=DbHelper.get(DbConfig.getDbSource(), c->{
            t_publish_model publish=new t_publish_base_dal().get(c,id);
            return publish;
        });
        if(publishModel==null){
            return ServerResponse.createByErrorMessage("发布记录不存在");
        }

        StateEngine engine=new StateEngine().fromJsonWithOperate(nodeJson,type);
        if(engine==null){
            return ServerResponse.createByErrorMessage("序列化出错了");
        }
        if(engine.getNodes().size()==0){
            return ServerResponse.createByErrorMessage("没有可用的节点");
        }

        try {
            //取出当前节节点
            var start= engine.getNodes().get(0);
            //当前操作类型
            String operateType=start.getOperateType();
            String nodeType=start.getType();
            //发布日志服务
            PublishLogService publishLogService=new PublishLogServiceImpl();
            //生成的文件名称
            String logFileNname=publishLogService.buildFileName(publishModel.getId(),operateType,nodeType,".txt");
            engine.setLogFileName(logFileNname);
            engine.createFolder();
            return ServerResponse.createBySuccess(logFileNname);
        } catch (IOException e) {
            e.printStackTrace();
            return ServerResponse.createByErrorMessage("生成文件夹文件出错了:"+e.getMessage());
        }
    }


    /**
     * 通过发布id生成对应的文件夹和文件名称
     * @param type
     * @param id
     * @return
     */
    @PostMapping("/generatePublishLogFileByPublishId")
    @ResponseBody
    public ServerResponse  generatePublishLogFileByPublishId(@RequestParam(required = true) String type,
                                                  @RequestParam(required = true) Integer id){
        String publishTypeDesc;
        //参数校验
        if(!("normal".equals(type))&&!("rollback".equals(type))){
            return ServerResponse.createByErrorMessage("参数类型错误");
        }
        //用户会话校验
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }

        //发布记录校验
        t_publish_model publishModel=DbHelper.get(DbConfig.getDbSource(), c->{
            t_publish_model publish=new t_publish_base_dal().get(c,id);
            return publish;
        });
        if(publishModel==null){
            return ServerResponse.createByErrorMessage("发布记录不存在");
        }


        StateEngine engine=null;
        //参数校验
        if("normal".equals(type)){
            publishTypeDesc="正常发布流程";
             engine=new StateEngine().fromJsonWithOperate(publishModel.getCommonFlow(),type);
        }else{
            publishTypeDesc="回滚流程";
            engine=new StateEngine().fromJsonWithOperate(publishModel.getRollFlow(),type);
        }

        try {
            //取出当前节节点
            var start= engine.getNodes().get(0);
            //当前操作类型
            String operateType=start.getOperateType();
            String nodeType=start.getType();
            //发布日志服务
            PublishLogService publishLogService=new PublishLogServiceImpl();
            //生成的文件名称
            String logFileNname=publishLogService.buildFileName(publishModel.getId(),operateType,nodeType,".txt");
            //设置日志文件名称
            engine.setLogFileName(logFileNname);
            //创建目录+文件
            engine.createFolder();
            return ServerResponse.createBySuccess(logFileNname);
        } catch (IOException e) {
            e.printStackTrace();
            return ServerResponse.createByErrorMessage("生成文件夹文件出错了:"+e.getMessage());
        }
    }


    /**
     * 测试发布，测试整个发布流程,可以从任意节点开始
     * 测试发布，要生成对应的发布日志明细
     * @param nodeJson  当前节点json
     * @return
     */
    @PostMapping("/testPublish")
    @ResponseBody
    public ServerResponse testPublish(@RequestParam(required = true) String nodeJson,
                                      @RequestParam(required = true) String type,
                                      @RequestParam(required = true) Integer id,
                                      @RequestParam(required = true)String logFileNname){

        String publishTypeDesc;
        //参数校验
        if(!("normal".equals(type))&&!("rollback".equals(type))){
            return ServerResponse.createByErrorMessage("参数类型错误");
        }

        //参数校验
        if("normal".equals(type)){
            publishTypeDesc="正常发布流程";
        }else{
            publishTypeDesc="回滚流程";
        }


        //用户会话校验
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }

        //发布记录校验
        t_publish_model publishModel=DbHelper.get(DbConfig.getDbSource(), c->{
            t_publish_model publish=new t_publish_base_dal().get(c,id);
            return publish;
        });
        if(publishModel==null){
            return ServerResponse.createByErrorMessage("发布记录不存在");
        }

        //根据json序列化当前节点node
        try {
            StateEngine engine=new StateEngine().fromJsonWithOperate(nodeJson,type);
            if(engine==null){
                return ServerResponse.createByErrorMessage("序列化出错了");
            }
            if(engine.getNodes().size()==0){
                return ServerResponse.createByErrorMessage("没有可用的节点");
            }
            //取出当前节节点
            var start= engine.getNodes().get(0);
            //发布日志服务
            PublishLogService publishLogService=new PublishLogServiceImpl();
            //日志明细内容text
            String content=publishLogService.buildPublishContent(publishModel,start.getText(),publishTypeDesc);
            long startTime = System.currentTimeMillis();
            engine.setLogFileName(logFileNname);
            //执行节点发布
            engine.currentNodeflow();
            //记录明细记录
            ServerResponse response= new PublishLogServiceImpl().record(id,logFileNname,content,
                    user.getName(),
                    System.currentTimeMillis()-startTime
            );
            if(!response.isSuccess()){
                return response;
            }
            return ServerResponse.createBySuccess("发布成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ServerResponse.createByErrorMessage("发布出错:"+e.getMessage());
        }
    }




    /**
     * 仅发布操作
     * 仅发布当前流程，如果出现异常（报警），就终止发布。
     * @param id
     * @param type
     * @return
     */
    @PostMapping("/publishOper")
    @ResponseBody
    public ServerResponse publish(@RequestParam(required = true) Integer id,
                                  @RequestParam(required = true) String type,
                                  @RequestParam(required = true)String logFileNname){

        String publishTypeDesc;
        //参数校验
        if(!("normal".equals(type))&&!("rollback".equals(type))){
            return ServerResponse.createByErrorMessage("参数类型错误");
        }

        //参数校验
        if("normal".equals(type)){
            publishTypeDesc="正常发布流程";
        }else{
            publishTypeDesc="回滚流程";
        }

        //用户会话校验
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }

        //发布记录校验
        t_publish_model publishModel= DbHelper.get(DbConfig.getDbSource(), c->{
            t_publish_model publisModel=new t_publish_base_dal().get(c,id);
            return publisModel;
        });
        if(publishModel==null){
            return ServerResponse.createByErrorMessage("发布记录信息不存在:"+id);
        }

        StateEngine engine=new StateEngine();
        if("normal".equals(type)){
            engine=engine.fromJsonWithOperate(publishModel.getCommonFlow(),type);
        }else if("rollback".equals(type)){
            engine=engine.fromJsonWithOperate(publishModel.getRollFlow(),type);
        }else{
            return ServerResponse.createByErrorMessage("不支持改类型操作:"+type);
        }
        engine.flow();
        //取出当前节节点
        var start= engine.getNodes().get(0);
        //发布日志服务
        PublishLogService publishLogService=new PublishLogServiceImpl();
        //日志明细内容text
        String content=publishLogService.buildPublishContent(publishModel,start.getText(),publishTypeDesc);
        long startTime = System.currentTimeMillis();
        engine.setLogFileName(logFileNname);
        //执行节点发布
        engine.flow();
        //记录明细记录
        ServerResponse response= new PublishLogServiceImpl().record(id,logFileNname,content,
                user.getName(),
                System.currentTimeMillis()-startTime
        );
        if(!response.isSuccess()){
            return response;
        }
        return ServerResponse.createBySuccessMessage("流程执行成功");
    }



    /**
     * 自动发布操作
     * 先执行正常发布流程，如果出现异常（报警），则执行回滚流程。如果回滚流程失败，则终止（报警）。需要人工介入处理。
     * @param id
     * @return
     */
    @PostMapping("/autoPublishOper")
    @ResponseBody
    public ServerResponse autoPublishOper(@RequestParam(required = true) Integer id){
        StateEngine engine=new StateEngine();
        t_publish_model publishModel= DbHelper.get(DbConfig.getDbSource(), c->{
            t_publish_model publisModel=new t_publish_base_dal().get(c,id);
            return publisModel;
        });
        if(publishModel==null){
            return ServerResponse.createByErrorMessage("发布记录信息不存在:"+id);
        }
        //用户会话校验
        t_user_model user=User.getCurrent();
        if(user==null){
            return ServerResponse.createByErrorMessage("用户会话已失效");
        }
        //发布日志服务
        PublishLogService publishLogService=new PublishLogServiceImpl();

        try {
            //生成的文件名称
            String logFileNname=publishLogService.buildFileName(publishModel.getId(),"auto-normal","start",".txt");
            //日志明细内容text
            String content=publishLogService.buildPublishContent(publishModel,"开始节点","自动化正常发布流程");

            long startTime = System.currentTimeMillis();
            engine=engine.fromJsonWithOperate(publishModel.getCommonFlow(),"normal");
            //设置日志文件名称
            engine.setLogFileName(logFileNname);
            //创建目录+文件
            engine.createFolder();
            //执行流程
            engine.flow();

            //记录明细记录
            ServerResponse response= new PublishLogServiceImpl().record(id,logFileNname,content,
                    user.getName(),
                    System.currentTimeMillis()-startTime
            );
            if(!response.isSuccess()){
                return response;
            }
            return ServerResponse.createBySuccessMessage("正常流程执行成功");
        } catch (Exception e) {
            //正常发布出错了
            e.printStackTrace();
            return callback(publishLogService,publishModel,engine,user);
        }
    }

    private ServerResponse callback(PublishLogService publishLogService, t_publish_model publishModel, StateEngine engine, t_user_model user){
        try {
            //生成的文件名称
            String logFileNname = publishLogService.buildFileName(publishModel.getId(), "auto-rollback", "start", ".txt");
            //日志明细内容text
            String content = publishLogService.buildPublishContent(publishModel, "开始节点", "自动化回滚发布流程");

            long startTime = System.currentTimeMillis();
            //出现异常则执行回滚流程
            engine = engine.fromJsonWithOperate(publishModel.getRollFlow(), "rollback");
            //设置日志文件名称
            engine.setLogFileName(logFileNname);
            //创建目录+文件
            engine.createFolder();
            //执行流程
            engine.flow();
            //记录明细记录
            ServerResponse response = new PublishLogServiceImpl().record(publishModel.getId(), logFileNname, content,
                    user.getName(),
                    System.currentTimeMillis() - startTime
            );
            if (!response.isSuccess()) {
                return response;
            }
            return ServerResponse.createBySuccessMessage("回滚流程执行成功");
        }catch (Exception e){
            e.printStackTrace();
            return ServerResponse.createBySuccessMessage("回滚流程执行出错成功");
        }
    }


    /**
     * Java_判断文件是否写入完成
     * @return
     */
    @GetMapping("/checkLogStatus")
    @ResponseBody
    public ServerResponse checkLogStatus(@RequestParam(required = true) String fileName){
        try {
            long oldLength;
            long newLength;
            String filePath=new StateEngine().getAbsouteFilePath(fileName);
            File logFile=new  File(filePath);
            //判断文件是否写入完成
            oldLength = logFile.length();
            log.info("File Uploading >> : " + oldLength);
            Thread.sleep(3000);
            newLength =logFile.length();
            log.info("File Uploading >>: " + newLength);
            if(oldLength != newLength) {
                //实时读取
                return ServerResponse.createBySuccess("readRealTime");
            }else{
                return ServerResponse.createBySuccess("readAll");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return ServerResponse.createByErrorMessage("执行出错:"+e.getMessage());
        }
    }




    /**
     * 读取日志
     * @return
     */
    @GetMapping("/readLog")
    @ResponseBody
    public ServerResponse readLog(String type,String fileName,Long lastTimeFileSize){
        try {
            String filePath=new StateEngine().getAbsouteFilePath(fileName);
            File logFile = new File(filePath);
            Map<String, Object> fileMap = Maps.newHashMap();
            if(!logFile.exists()){
                //返回空的文件对象
                return ServerResponse.createBySuccess(fileMap);
            }
            if ("all".equals(type)) {
                StringBuilder stringBuilder=new StringBuilder();
                FileReader fileReader = new FileReader(filePath);
                String content = fileReader.readString();
                stringBuilder.append(content);
                fileMap.put("content", stringBuilder.toString());
                return ServerResponse.createBySuccess(fileMap);
            } else {
                //随机读写
                //指定文件可读可写 
                final RandomAccessFile randomFile = new RandomAccessFile(logFile, "rw");
                //获得变化部分的 
                randomFile.seek(lastTimeFileSize);
                String tmp = "";
                if ((tmp = randomFile.readLine()) != null) {
                    String content = new String(tmp.getBytes("ISO8859-1"));
                    lastTimeFileSize = randomFile.getFilePointer();
                    fileMap.put("nextRandomIndex", lastTimeFileSize);
                    fileMap.put("content", content);
                }
                return ServerResponse.createBySuccess(fileMap);
            }
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}
